module ESI
  class ParserMachine

%%{
  machine esi;

  action begin {
    @mark = p
  }
  action finish {
  }

  # record the position of the start tag
  action see_start_tag {
    @tag_text = data[@mark,p-@mark]
    @tag_info = {} # store the tag attributes
    @tag_info[:name] = @tag_text.sub('<','').strip
    @tag_info[:attributes] = {}
#    puts "have esi tag at #{p}=>#{@mark}:#{data[p,1].inspect} with data #{@tag_text.inspect}"
    @mark = p
  }

  action see_end_tag {
#    puts "parsed esi tag at #{p}=>#{@mark}:#{data[p,1].inspect} with data #{@tag_text.inspect}"
    @start_tag.call @tag_info[:name], @tag_info[:attributes] if @start_tag
    @end_tag.call @tag_info[:name] if @end_tag
    @attr_key = nil
    @attr_value = nil
    @tag_text = nil
    @tag_info = nil
  }
  
  action see_block_start_with_attributes {
#    puts "parsed esi tag at #{p}=>#{@mark}:#{data[p,1].inspect} with data #{@tag_text.inspect}"
    @start_tag.call @tag_info[:name], @tag_info[:attributes] if @start_tag
    @attr_key = nil
    @attr_value = nil
    @tag_text = nil
    @tag_info = nil
  }

  action see_attribute_key {
    @attr_key = data[@mark,p-@mark]
    @mark += (@attr_key.size)
    @attr_key.gsub!(/^['"]/,'')
    @attr_key.strip!
#    puts "SeeAttributeKey: #{data[@mark,1].inspect}: #{data[p,1].inspect} #{@attr_key.inspect}"
  }

  action see_attribute_value {
    @attr_value = data[@mark,p-@mark]
    @attr_value.strip!
    @attr_value.gsub!(/^=?\s*['"]/,'')
    @attr_value.gsub!(/['"]$/,'')

    @tag_info[:attributes][@attr_key] = @attr_value
#    puts "SeeAttributeValue: #{p} #{data[@mark,1].inspect}: #{data[p,1].inspect} #{@attr_key.inspect} => #{@attr_value.inspect}"
    @mark = p
  }

  action block_start_tag {
    tag_text = data[@mark+1,p-@mark-1]
    @start_tag.call tag_text, {} if @start_tag
#    puts "Block start: #{p} #{tag_text}"
  }

  action block_end_tag {
    tag_text = data[@mark+2,p-@mark-2]
    @end_tag.call tag_text if @end_tag
#    puts "Block end: #{p} #{tag_text}"
  }

  action echo {
#    print " [#{data[p,1].inspect}:#{cs}] " if $debug

    case cs
    when 0
      # NOTE: state 12 is the character state before <esi:try>, state 7 is the character before </esi:try>
      #                                                     |                                           |
      #                                                     - state 12                                  - state 7
      # state 60 is for empty inline tags e.g.
      # <esi:include/>
      #              |
      #              - 60

      if @prev_state != 12 and @prev_state != 7
        if !@prev_buffer.empty? and (@prev_state != (esi_en_main + 1)) and @prev_state != 60
          stream_buffer @prev_buffer
        end
        stream_buffer data[p,1]
      end
      @prev_buffer = ""
    else
      @prev_buffer << data[p,1]
    end
    @prev_state = cs
  }

  include esi_common_parser "common.rl";
}%%

    def initialize
      %% write data;
    end

    # process a block of esi tags
    def process(data)
      if @data
  #      puts "append : #{@mark} : #{p}"
        data = @data + data
        p = @data.length
      end
      @mark ||= 0
      p ||= 0
      pe ||= data.length
      @cs ||= esi_start
      cs = @cs
  #    puts "process: #{cs.inspect} :start #{data.inspect}, #{p}"
      %% write exec;
      @cs = cs
      if( @cs != esi_start && @cs != 0 )
  #    puts "append process: #{@cs.inspect}"
        @data = data
      else
        @data = nil
      end
    end

    def finish
      %% write eof;
    end

  end

end
